package ServerApp;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Calendar;

// test

@SuppressWarnings("serial")
public class Server extends UnicastRemoteObject implements IServer {

	public static int TIMEOUT_MINUTES = 5;
	public static String SERVICE_NAME = "Calendar";
	public static String USER_DB = "UserDB.xml";
	public static String MEETING_DB = "Meetings.xml";
	public static String ADMIN_NAME = "admin";
	public static String ADMIN_PSW = "admin_psw";
	private static Server instance;
	private ArrayList<TimeOut> activeUsers;
	
	protected Server() throws RemoteException {
		super();
		System.out.println("Starting a Server");

		try {
			if (!new File(USER_DB).exists()) {
				AddUser(new User("admin", "admin", true));
			}

			if (!new File(MEETING_DB).exists()){
				File f = new File(MEETING_DB);
				try {
					f.createNewFile();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			activeUsers = new ArrayList<TimeOut>();
			
//			if (!new File(MEETING_DB).exists()) {
//				Calendar c1 = Calendar.getInstance();
//				c1.set(2012, 7, 8);
//				Meeting m1 = new Meeting("meeting 1", "location 1",
//						c1.getTime(), "details 1");
//				c1.set(2012, 7, 15);
//				Meeting m2 = new Meeting("meeting 2", "location 2",
//						c1.getTime(), "details 2");
//				Meeting m4 = new Meeting("meeting 4", "location 4",
//						c1.getTime(), "details 4");
//				c1.set(2012, 7, 22);
//				Meeting m3 = new Meeting("meeting 3", "location 3",
//						c1.getTime(), "details 3");
//
//				AddMeeting(m1);
//				AddMeeting(m2);
//				AddMeeting(m3);
//				AddMeeting(m4);
//			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		System.out.println("Server started");
	}

	public static Server GetInstance() throws RemoteException{
		if(instance == null)
			instance = new Server();
		return instance;
	}
	
	// ********************* RMI Methods *********************
	@Override
	public User login(String username, String password) throws RemoteException {
		System.out.println("% login");
		try {
			System.out.println("get user array list");
			ArrayList<User> l = getUserArrayList();
			for (User user : l) {
				System.out.println("verify user credential");
				if (user.verifyCredentials(username, password)){
					if(activeUsers.contains(user)){
						activeUsers.remove(user);
					}
					else{
						TimeOut t = new TimeOut();
						t.user = user;
						t.activity = Calendar.getInstance().getTimeInMillis();
						activeUsers.add(t);
					}
					return user;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public ArrayList<Meeting> getMeetings(User currentUser) {
		System.out.println("% getMeetings");
		if(verifyTimeout(currentUser))
			return null;
		try {
			ArrayList<Meeting> l = getMeetingList();
			return l;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public boolean addNewUser(User user, User currentUser) throws RemoteException {
		System.out.println("% addNewUser");
		if(verifyTimeout(currentUser))
			return false;
		try {
			AddUser(user);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	public boolean addNewMeeting(Meeting meeting, User currentUser) throws RemoteException {
		System.out.println("% addNewMeeting");
		if(verifyTimeout(currentUser))
			return false;
		try {
			AddMeeting(meeting);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public boolean deleteMeeting(Meeting meeting, User currentUser) throws RemoteException {
		System.out.println("% deleteMeeting");
		if(verifyTimeout(currentUser))
			return false;
		try {
			RemoveMeeting(meeting);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public boolean logOut(User user) throws RemoteException {
		activeUsers.remove(user);
		return true;
	}
	
	// ********************* End RMI Methods *********************

	private boolean verifyTimeout(User u){
		if(activeUsers.contains(u)){
			long val1 = Calendar.getInstance().getTimeInMillis();
			long val2 = activeUsers.get(activeUsers.indexOf(u)).activity;
			long diff = Math.abs(val1 - val2);
			long diffHours = diff / (60 * 60 * 1000);
			long diffMinutes = diff / (60 * 1000);
			long diffSeconds = diff / 1000;
			
			if(diffHours > 0 || diffMinutes > TIMEOUT_MINUTES /*|| diffSeconds > 10*/)
				return true;
			else
				return false;
		}
		else return false;
	}
	
	@SuppressWarnings("unchecked")
	private ArrayList<User> getUserArrayList() throws FileNotFoundException {
		System.out.println("% getUserArrayList");
		XMLDecoder d = new XMLDecoder(new BufferedInputStream(
				new FileInputStream(USER_DB)));
		ArrayList<User> l = (ArrayList<User>) d.readObject();
		d.close();
		return l;
	}

	private void writeUserArrayList(ArrayList<User> list)
			throws FileNotFoundException {
		System.out.println("% writeUserArrayList");
		XMLEncoder e = new XMLEncoder(new BufferedOutputStream(
				new FileOutputStream(USER_DB)));
		e.writeObject(list);
		e.close();
	}

	private void AddUser(User u) throws FileNotFoundException {
		System.out.println("% AddUser");
		ArrayList<User> l;
		if (new File(USER_DB).exists())
			l = getUserArrayList();
		else
			l = new ArrayList<User>();
		l.add(u);
		writeUserArrayList(l);
	}

	@SuppressWarnings("unchecked")
	private ArrayList<Meeting> getMeetingList() throws FileNotFoundException {
		System.out.println("% getMeetingList");
		File f = new File(MEETING_DB);
		if(f.length() == 0)
			return null;
		XMLDecoder d = new XMLDecoder(new BufferedInputStream(
				new FileInputStream(MEETING_DB)));
		ArrayList<Meeting> l = (ArrayList<Meeting>) d.readObject();
		d.close();
		return l;
	}

	private void writeMeetingArrayList(ArrayList<Meeting> list)
			throws FileNotFoundException {
		System.out.println("% writeMeetingArrayList");
		XMLEncoder e = new XMLEncoder(new BufferedOutputStream(
				new FileOutputStream(MEETING_DB)));
		e.writeObject(list);
		e.close();
	}

	private void AddMeeting(Meeting m) throws FileNotFoundException {
		System.out.println("% AddMeeting");
		ArrayList<Meeting> l = null;
		if (new File(MEETING_DB).exists())
			l = getMeetingList();
		if(l == null)
			l = new ArrayList<Meeting>();
		l.add(m);
		writeMeetingArrayList(l);
	}

	private void RemoveMeeting(Meeting m) throws FileNotFoundException{
		ArrayList<Meeting> l = getMeetingList();
		if(l != null){
			l.remove(m);
			writeMeetingArrayList(l);
		}
	}
	
	public class TimeOut{
		User user;
		long activity;
		
		@Override
		public boolean equals(Object obj) {
			return user.equals((User)obj);
		}
	}
	
	// **************** MAIN ***********************
	public static void main(String[] args) {

		try {

			System.out.println("% Server main");

			LocateRegistry.createRegistry(Registry.REGISTRY_PORT);

			Naming.rebind(SERVICE_NAME, Server.GetInstance());

			System.out.println("% End server main");

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	// *********************************************

}
